#include <drivers/uart.h>
#include <kernel/initcall.h>
#include <drivers/irq.h>
#include <drivers/gpio.h>
#include <drivers/clock.h>
#include <kernel/syslog.h>
#include <kernel/fdt.h>
#include <mm/mem.h>
#include <irqnum.h>
#include <stdio.h>


extern char uart_recv_char;


static void x86_64_uart_start(struct uart_device *pdev)
{

}


static void x86_64_uart_stop(struct uart_device *pdev)
{

}


static void x86_64_uart_put_char(struct uart_device *pdev, char ch)
{
    printf("%c", ch);
    fflush(stdout);
}


static void x86_64_uart_put_string(struct uart_device *pdev, char *str)
{
    printf("%s", str);
    fflush(stdout);
}


static char x86_64_uart_get_char(struct uart_device *pdev)
{
    return uart_recv_char;
}


static void x86_64_uart_set_baudrate(struct uart_device *pdev, uint32_t baudrate)
{

}


struct uart_ops x86_64_uart_ops = {
    .start = x86_64_uart_start,
    .stop = x86_64_uart_stop,
    .put_char = x86_64_uart_put_char,
    .put_string = x86_64_uart_put_string,
    .get_char = x86_64_uart_get_char,
    .set_baudrate = x86_64_uart_set_baudrate,
};


static int x86_64_uart_probe(struct platform_device *dev)
{
    fdt_node_t *uart_node = dev->device.dt_node;

    size_t irq_num = 0;
    if(fdt_read_prop_int(uart_node, "interrupts", &irq_num)) {
        PLATFORM_SYSLOG_ERROR("uart: no interrupts property");
    }

    size_t baudrate = 0;
    if(fdt_read_prop_int(uart_node, "baudrate", &baudrate)) {
        PLATFORM_SYSLOG_ERROR("uart: no baudrate property");
        return -1;
    }

    struct uart_gpio pins = {-1, -1};
    if(uart_master_get_gpio_from_fdt(uart_node, &pins)) {
        return -1;
    }

    struct uart_master *master = kmalloc(sizeof(struct uart_master));
    if(master == NULL) {
        PLATFORM_SYSLOG_ERROR("uart: kmalloc failed");
        return -1;
    }

    master->ops = &x86_64_uart_ops;
    master->pins = pins;
    master->io_base = dev->io_base;

    platform_set_drvdata(dev, master);

    fdt_node_t *child =  NULL;
    fdt_for_each_node_child(uart_node, child) {
        struct uart_device *udev = kmalloc(sizeof(struct uart_device));
        if(udev == NULL) {
            PLATFORM_SYSLOG_ERROR("uart: kmalloc failed");
            return -1;
        }

        const char *comp = fdt_read_prop_string(child, "compatible");
        if(comp == NULL) {
            PLATFORM_SYSLOG_ERROR("uart: no compatible property");
            return -1;
        }

        udev->device.name = uart_node->name;
        udev->device.compatible = comp;
        udev->device.driver = NULL;
        udev->master = master;

        if(uart_device_register(udev) < 0) {
            PLATFORM_SYSLOG_ERROR("uart: uart_device_register failed");
            return -1;
        }

        if(irq_num) {
            udev->irq_num = irq_num;
        }
    }

    return 0;
}


static void x86_64_uart_remove(struct platform_device *dev)
{

}


static struct of_device_id x86_64_uart_match[] = {
    {.compatible = "x86_64-uart", },
    { }
};


static struct platform_driver x86_64_uart_driver = {
    .driver = {
        .name = "x86_64-uart",
        .of_match_table = x86_64_uart_match,
    },
    .probe = x86_64_uart_probe,
    .remove = x86_64_uart_remove,
};


static int x86_64_uart_driver_register(void)
{
    return platform_driver_register(&x86_64_uart_driver);
}


core_initcall(x86_64_uart_driver_register);
